#include "samplers.fx"
#include "shading.fx"

struct Vertex
{
	float3	position	:	POSITION;
	float3	normal		:	NORMAL;
	float3	tangent		:	TANGENT;
	float2	tex			:	TEX_COORDS;
};
//____________
struct ControlPoint
{
	float3	position	:	POSITION;
};
//____________
struct triTessFactors
{
	float	edge[3]		:	SV_TessFactor;
	float	inside[1]	:	SV_InsideTessFactor;
};
//____________
struct DS_OUT
{
	float4	position	:	SV_POSITION;
	float3	color		:	COLOR;
	float3	normal		:	NORMAL;
};

//________ V A R I A B L E S____________________________________________________________________________

Texture2D	texTerrainHeight;
float4x4	m_World;
float4x4	m_WorldViewProj;
float4x4	mViewProj;
float		fInsideTess;
float		fEdgeTess;
float		fRadius;



//________ S H A D E R S________________________________________________________________________________


float getTerrainHeight(float3 wpos)
{
	float GRID_WIDTH = 200.0f;

	float u = wpos.x / GRID_WIDTH + 0.5f;
	float v = wpos.y / GRID_WIDTH + 0.5f;

	return texTerrainHeight.SampleLevel(s_linear, float2(u,v), 0).x;

}


Vertex VS_Sphere(Vertex vin)
{
	return vin;
}
//_______________
											// ctrl points per patch
triTessFactors ConstantHS_Sphere(InputPatch<Vertex, 3> patch, uint patchID : SV_PrimitiveID) // Constant Hull Shader
{
	triTessFactors tout;

	tout.edge[0] = fEdgeTess;
	tout.edge[1] = fEdgeTess;
	tout.edge[2] = fEdgeTess;
	
	tout.inside[0] = fInsideTess;

	return tout;
}
//_______________

[domain("tri")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("ConstantHS_Sphere")]
[maxtessfactor(64.0f)]
Vertex		 ControlPointHS_Sphere_Integer(InputPatch<Vertex, 3> ctrlPoints,		uint ctrlPoint_index : SV_OutputControlPointID, 
																			uint patchId		 : SV_PrimitiveID)
{
	return ctrlPoints[ctrlPoint_index];
}
//_______________


[domain("tri")]
[partitioning("fractional_even")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("ConstantHS_Sphere")]
[maxtessfactor(64.0f)]
Vertex		 ControlPointHS_Sphere_FracEven(InputPatch<Vertex, 3> ctrlPoints,		uint ctrlPoint_index : SV_OutputControlPointID, 
																			uint patchId		 : SV_PrimitiveID)
{
	return ctrlPoints[ctrlPoint_index];
}
//_______________

[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("ConstantHS_Sphere")]
[maxtessfactor(64.0f)]
Vertex		 ControlPointHS_Sphere_FracOdd(InputPatch<Vertex, 3> ctrlPoints,		uint ctrlPoint_index : SV_OutputControlPointID, 
																			uint patchId		 : SV_PrimitiveID)
{
	return ctrlPoints[ctrlPoint_index];
}
//_______________


[domain("tri")]
DS_OUT	DS_Sphere(triTessFactors tessFactors,	float3 barCoords : SV_DomainLocation, 
												const OutputPatch<Vertex, 3> ctrlPoints)
{
	DS_OUT dout;
	
	// find pos of generated point
	float3 pos =	barCoords.x * ctrlPoints[0].position + 
					barCoords.y * ctrlPoints[1].position + 
					barCoords.z * ctrlPoints[2].position;


	// center of the triangle
	float3 cntr =	0.33f * ctrlPoints[0].position + 
					0.33f * ctrlPoints[1].position + 
					0.33f * ctrlPoints[2].position;


	// standard deviation from 0.33
	float dev =  (barCoords.x - 0.33f) * (barCoords.x - 0.33f);
		  dev += (barCoords.y - 0.33f) * (barCoords.y - 0.33f);
		  dev += (barCoords.z - 0.33f) * (barCoords.z - 0.33f);
		  dev /= 3.0f;

	dout.color = float3(dev, dev, dev);
	//

	float3 e1 = ctrlPoints[0].position - ctrlPoints[1].position;
	float3 e2 = ctrlPoints[0].position - ctrlPoints[2].position;
	
	// normal of the tri defned by the control points
	float3 triNormal = normalize(cross(e1, e2));


	dout.normal =	barCoords.x * ctrlPoints[0].normal + 
					barCoords.y * ctrlPoints[1].normal + 
					barCoords.z * ctrlPoints[2].normal;

	dout.color = float3(0,0,0);

	if (barCoords.x != 0.0f && barCoords.y != 0.0f && barCoords.z != 0.0f) {
		pos += (5.0f - dev*5) * triNormal * 0.05f;
		dout.color = float3(1,0,0);
		dout.normal = triNormal;
	}
	

	float3 wpos = float3(m_World._41, m_World._42, m_World._43);
				 
	pos.y  += getTerrainHeight(wpos) * 10.0f - 10.0f;
	dout.position = mul(float4(pos, 1.0f), m_WorldViewProj);

	return dout;

}

//_______________


float4 PS_Sphere(DS_OUT pin) : SV_TARGET
{
	
	return float4(pin.color * shade(pin.normal), 1.0f);

}


//###################################################################################


DS_OUT VS_NoTess(Vertex vin)
{
	DS_OUT dout = (DS_OUT)0;
	dout.position = mul(float4(vin.position, 1.0f), m_WorldViewProj);
	return dout;

}

float4 PS_NoTess(DS_OUT pin) : SV_TARGET
{
	return float4(1,0,0,1);
}

//###################################################################################


RasterizerState rs_Wireframe
{
	CullMode = Back;
	FillMode = Wireframe;
};


technique11 TessSphere_Integer
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_Sphere()   ) );
		SetHullShader ( CompileShader ( hs_5_0, ControlPointHS_Sphere_Integer() ) );
		SetDomainShader( CompileShader ( ds_5_0, DS_Sphere() ) );
		SetGeometryShader( NULL ) ;
		SetPixelShader( CompileShader( ps_5_0,   PS_Sphere()   ) );
	}
}
technique11 TessSphere_FracEven
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_Sphere()   ) );
		SetHullShader ( CompileShader ( hs_5_0, ControlPointHS_Sphere_FracEven() ) );
		SetDomainShader( CompileShader ( ds_5_0, DS_Sphere() ) );
		SetGeometryShader( NULL ) ;
		SetPixelShader( CompileShader( ps_5_0,   PS_Sphere()   ) );
	}
}

technique11 TessSphere_FracOdd
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_Sphere()   ) );
		SetHullShader ( CompileShader ( hs_5_0, ControlPointHS_Sphere_FracOdd() ) );
		SetDomainShader( CompileShader ( ds_5_0, DS_Sphere() ) );
		SetGeometryShader( NULL ) ;
		SetPixelShader( CompileShader( ps_5_0,   PS_Sphere()   ) );
	}
}



technique11 NoTess
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_NoTess()   ) );
		SetPixelShader( CompileShader( ps_5_0,   PS_NoTess()   ) );
	}


}